import status from "./status.js";
import config from "../env/config.js";
import migrationsDir from "../env/migrationsDir.js";
import lock from "../utils/lock.js";

export default async (db, client) => {
  const isBlockRollback = global.options?.block;
  const downgraded = [];
  const statusItems = await status(db);
  const appliedItems = statusItems.filter(item => item.appliedAt !== "PENDING");
  const lastAppliedItem = appliedItems[appliedItems.length - 1];

  let itemsToRollback = [];

  if (isBlockRollback && lastAppliedItem.migrationBlock) {
    itemsToRollback = appliedItems.filter(item => item.migrationBlock === lastAppliedItem.migrationBlock).reverse();
  } else {
    itemsToRollback = [lastAppliedItem];
  }

  const rollbackItem = async item => {
    if (item) {
      try {
        const migration = await migrationsDir.loadMigration(item.fileName);
        await migration.down(db, client);

      } catch (err) {
        throw new Error(
          `Could not migrate down ${item.fileName}: ${err.message}`
        );
      }
      const { changelogCollectionName } = await config.read();
      const changelogCollection = db.collection(changelogCollectionName);
      try {
        await changelogCollection.deleteOne({ fileName: item.fileName });
        downgraded.push(item.fileName);
      } catch (err) {
        throw new Error(`Could not update changelog: ${err.message}`);
      }
    }
  }

  if (await lock.exist(db)) {
    throw new Error("Could not migrate down, a lock is in place.");
  }
  try {
    await lock.activate(db);
  } catch(err) {
    throw new Error(`Could not create a lock: ${err.message}`);
  }

  try {
    for (const item of itemsToRollback) {
      await rollbackItem(item);
    }
  } catch (err) {
    await lock.clear(db);
    throw err;
  }

  await lock.clear(db);
  return downgraded;
};
